import 'dart:io';

import 'package:agora_rtc_engine/media_recorder.dart';
import 'package:agora_rtc_engine/rtc_engine.dart';
import 'package:agora_rtc_engine/src/impl/media_recorder_impl.dart';
import 'package:star_author/config/base_configuration.dart';
import 'package:star_author/controller/app_package_info_getx_controller.dart';
import 'package:star_author/env/env_config.dart';
import 'package:star_author/event_bus/global_event_bus.dart';
import 'package:star_author/provider/config.dart';
import 'package:star_author/utils/utils.dart';
import 'package:dio/dio.dart';
import 'package:dio_log/interceptor/dio_log_interceptor.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart' as ft;
import 'package:get/get.dart' as getx;
import 'package:get/get_core/src/get_main.dart';
import 'package:md5_file_checksum/md5_file_checksum.dart';
import 'package:package_info/package_info.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

import '../event_bus/open_live_current_event.dart';
import '../handler/common_permission_handler.dart';
import '../provider/user.dart';
import 'common_util/log_util.dart';
import 'common_util/shared_preferences/sp_util.dart';
import 'common_util/toast/custom_toast.dart';
import 'http/http_util.dart';

///todo:录制工具类
class MediaRecorderUtil {
  //把所有的upload请求标记 存放到一个map里面 下一次直接根据id来查询到对应到canceltoken来用于取消请求
  static Map<String, CancelToken?> requestMap = {};

  static bool openMediaRecord = true; //是否开启录制 默认开启

  static String localKey = "localVideoKey";

  static int anchorId = 0;

  static late MediaRecorderImpl mediaRecorderImpl;

  static int get shortestRecordingVideoTime {
    return Provider.of<ConfigProvider>(getx.Get.context!, listen: false)
            .get("settings")['shortest_recording_video_time'] ??
        5;
  }

  static ft.FToast fToast = ft.FToast();

  static get longestMinutes {
    return (double.parse(StreamConf.channelModel?.longest_minutes ?? "15")
            .toInt()) *
        60 *
        1000;
  }

  //录制存放地址
  static Future<String> getStoragePath() async {
    String dirloc = "";
    if (Platform.isAndroid) {
      dirloc = (await getApplicationSupportDirectory()).path + '/huiliu/';
    } else {
      dirloc = (await getApplicationDocumentsDirectory()).path + '/huiliu/';
    }
    return dirloc;
  }

  //获取IOS日志地址
  static uploadLogLogFile() async {
    String path = "";
    if (Platform.isAndroid) {
      // /storage/emulated/0/Android/data/net.huiliu.app/files getExternalStorageDirectories
      List<Directory>? list = (await getExternalStorageDirectories())?.toList();
      if (list != null) {
        if (list.length > 0) {
          path = list[0].path + "/";
        }
      }
    } else if (Platform.isIOS) {
      path = (await getLibraryDirectory()).path + "/Caches/";
    }
    Directory directory = Directory(path);
    String agorasdk = "agorasdk.log";
    File agora_file = File(path + agorasdk);
    if (agora_file.existsSync()) {
      final formData = FormData.fromMap(
        {
          'file':
              await MultipartFile.fromFile(path + agorasdk, filename: agorasdk),
        },
      );
      HttpUtil.commonRequest(
        Get.context!,
        '/api/v1/app_crash_logs',
        method: HttpUtil.MethodPost,
        data: formData,
      ).then((value) {
        CustomToast.show('上传成功!');
      });
    } else {
      CustomToast.show('暂时没有日志!');
    }
  }

  //录制配置
  static Future<MediaRecorderConfiguration> GetMediaRecorderConfiguration(
      id) async {
    String filePath = await getStoragePath();
    Directory directory = Directory(filePath);
    if (!directory.existsSync()) {
      //判断文件目录是否存在
      directory.createSync();
    }

    String path = filePath +
        "/" +
        id.toString() +
        ".mp4"; //根据回流单id生成视频  用于后面获取回流单id 不然只会获取到最后点击的视频的id
    int recorderInfoUpdateInterval = 1000;
    fToast.init(getx.Get.context!);
    return MediaRecorderConfiguration(
        storagePath: path,
        containerFormat: MediaRecorderContainerFormat.MP4,
        streamType: MediaRecorderStreamType.Both,
        maxDurationMs: longestMinutes,
        //讲解录制最大时间
        recorderInfoUpdateInterval: recorderInfoUpdateInterval);
  }

  static RecorderInfo? info;

  static void showCustomToast(String title, String image) {
    Widget toast = Container(
      padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 18.w),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8.w),
        color: Colors.black.withOpacity(0.5),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.asset(
            image,
            width: 30.w,
            height: 30.w,
          ),
          SizedBox(
            width: 10.w,
          ),
          Text(
            title,
            style: TextStyle(
              fontSize: 14.sp,
              color: Colors.white,
            ),
          ),
        ],
      ),
    );
    fToast.showToast(
      child: toast,
      gravity: ft.ToastGravity.CENTER,
      toastDuration: Duration(seconds: 2),
    );
  }

  //同步删除本地视频页面的展示
  static deleteLocalPage(backflow_id) {
    List<Map> localVideoList =
        SpUtil.getObjectList(MediaRecorderUtil.localKey) ?? [];
    localVideoList.removeWhere(
        (element) => element['id'].toString() == backflow_id.toString());
    SpUtil.putObjectList(MediaRecorderUtil.localKey, localVideoList);
  }

  //开始录制
  static startRecording(backflow_id) async {
    String path = await getStoragePath();
    if (openMediaRecord) {
      if (mediaRecorderImpl != null) {
        File file = File(path); //判断文件是否存在 存在则删除
        if (file.existsSync()) {
          file.deleteSync();
          deleteLocalPage(backflow_id);
        }
        bool checkPermission1 = await Permission.storage.isGranted;
        if (checkPermission1 == false) {
          await Permission.storage.request();
          checkPermission1 = await Permission.storage.isGranted;
        }
        if (checkPermission1) {
          MediaRecorderConfiguration config =
              await MediaRecorderUtil.GetMediaRecorderConfiguration(
                  backflow_id);
          LogUtil.v('开始录制前,获取录播配置为:${config.toJson()}');
          mediaRecorderImpl.startRecording(config).catchError((error) {
            LogUtil.v("录制出错:${error}");
          });
        }
      }
    }
  }

  //关闭录制
  static stopRecording() {
    if (openMediaRecord) {
      if (mediaRecorderImpl != null) {
        mediaRecorderImpl.stopRecording();
      }
    }
  }

  //释放资源
  static releaseRecorder() {
    if (mediaRecorderImpl != null) {
      mediaRecorderImpl.releaseRecorder();
    }
  }

  static MediaRecorderObserver getCallBack() {
    //将声网获取到的录制时长从毫秒转换为秒
    int getSeconds(int ms) {
      return ms ~/ 1000;
    }

    return MediaRecorderObserver(
      onRecorderInfoUpdated: (RecorderInfo _info) {
        info = _info;
        StreamConf.currentGoodSeconds = getSeconds(info!.durationMs);
        GlobalEventBus.instance.bus
            .fire(OpenLiveCurrentEvent(getSeconds(info!.durationMs)));
        LogUtil.v(
            '录制文件名称:${info!.fileName},录制文件时长:${getSeconds(info!.durationMs)}S,录制文件大小:${formatFileSize(info!.fileSize)}');
      },
      onRecorderStateChanged:
          (RecorderState state, RecorderErrorCode error) async {
        if (state == RecorderState.Start) {
          CustomToast.show('开始录制');
          LogUtil.v("开始录制");
        } else if (state == RecorderState.Stop) {
          CustomToast.show('结束录制');
          String str = info!.fileName;
          int first = str.lastIndexOf('/');
          int last = str.lastIndexOf('.');
          String id = str.substring(first + 1, last);
          if (getSeconds(info!.durationMs) <= shortestRecordingVideoTime) {
            showCustomToast('视频时长小于$shortestRecordingVideoTime s，未保存录制',
                "images/ic_offer_failure_lt.png");

            File file = File(info!.fileName);
            file.deleteSync();
            deleteLocalPage(id);
            info = null;
            return;
          }

          List<Map> localVideoList =
              SpUtil.getObjectList(MediaRecorderUtil.localKey) ?? [];
          int indexWhere = localVideoList.indexWhere(
              (element) => element['id'].toString() == id.toString());
          if (indexWhere != -1) {
            if (!(localVideoList[indexWhere]['isAgree'] ?? true)) {
              File file = File(info!.fileName);
              file.deleteSync();
              deleteLocalPage(id);
              info = null;
              return;
            }
          }
          info = null;
          if (EnvConfig.isProd()) {
            upload(id, path: str);
          } else {
            upload(id, path: str);
          }
        } else if (state == RecorderState.Error) {
          if (error == RecorderErrorCode.None) {
            //一切正常
            CustomToast.show('录制完成');
            LogUtil.v('录制完成');
          } else if (error == RecorderErrorCode.WriteFailed) {
            //
            CustomToast.show('录制文件写入失败,请检查是否有权限');
            LogUtil.v('录制文件写入失败,请检查是否有权限');
          } else if (error == RecorderErrorCode.NoStream) {
            //
            CustomToast.show('没有可录制的音视频流或者录制的音视频流中断超过 5 秒');
            LogUtil.v('没有可录制的音视频流或者录制的音视频流中断超过 5 秒');
          } else if (error == RecorderErrorCode.OverMaxDuration) {
            //
            CustomToast.show('录制时长超出上限');
            LogUtil.v('录制时长超出上限');
          } else if (error == RecorderErrorCode.ConfigChanged) {
            //
            CustomToast.show('录制配置改变');
            LogUtil.v('录制配置改变');
          } else if (error == RecorderErrorCode.CustomStreamDetected) {
            //
            CustomToast.show('在通信场景下，捕捉到 v3.0.0 之前版本的用户发送的音视频流');
            LogUtil.v('在通信场景下，捕捉到 v3.0.0 之前版本的用户发送的音视频流');
          }
        }
      },
    );
  }

  //录制结束 调用文件上传接口
  static upload(
    id, {
    path: "/sdcard/download//2443.mp4",
    int count: 1,
    Function? success,
    Function? faild,
  }) async {
    //标记 当前上传的id
    requestMap[id.toString()] = CancelToken();
    //权限校验
    CommonPermissionHandler.storageRequestPermission(getx.Get.context!,
        () async {
      File file = File(path);
      int file_size = file.lengthSync();
      if (file_size == 0) {
        CustomToast.show('当前视频有问题,无法上传!');
        return;
      }
      //获取本地缓存的文件列表
      List<Map> localVideoList =
          SpUtil.getObjectList(MediaRecorderUtil.localKey) ?? [];
      //找到当前id的数据 需要用于metadata打点使用
      int index = localVideoList
          .indexWhere((element) => element['id'].toString() == id.toString());
      if (index != -1) {
        var user = Provider.of<UserProvider>(getx.Get.context!, listen: false);
        dynamic anchorId =
            localVideoList[index]['anchorId'] ?? 0; //绑定主播通过本地缓存的主播id来绑定
        Sentry.captureMessage("当前货品id:${id}--- 主播id为:$anchorId");
        PackageInfo? packageInfo =
            Get.find<AppPackageInfoGetXController>().packageInfo;
        String version = packageInfo?.version ?? "";
        Map<String, dynamic> default_map = {
          "filename": file.path,
          "content_type": "video/mp4",
          "byte_size": file_size,
        };
        FlutterFFprobe _flutterFFmpeg = new FlutterFFprobe();
        await _flutterFFmpeg.getMediaInformation(path).then((value) {
          double duration = double.tryParse(
                  (value.getMediaProperties()?['duration'] ?? "0")) ??
              0;
          default_map['duration'] = duration;
          List streams = value.getAllProperties()['streams'] ?? [];
          if (streams.length > 0) {
            int width = value.getAllProperties()['streams'][0]['width'] ?? 1;
            int height = value.getAllProperties()['streams'][0]['height'] ?? 1;
            default_map['width'] = width;
            default_map['height'] = height;
          }
        });
        String checksum = await Md5FileChecksum.getFileChecksum(filePath: path);
        default_map['checksum'] = checksum;
        LogUtil.v("default_map:$default_map");
        await HttpUtil.commonRequest(
          getx.Get.context!,
          '/api/v1/upload_directly_meta_information',
          queryParameters: default_map,
          method: HttpUtil.MethodPost,
          isLoading: false,
          isCatchErrorLogin: false,
          isErrorToast: false,
          timeout: 120,
          cancelToken: requestMap[id.toString()],
        ).then((value) async {
          Map headers = value.data['headers'];
          Dio dio = Dio()
            ..options = BaseOptions(
              connectTimeout: 999999,
              receiveTimeout: 999999,
              sendTimeout: 999999,
              headers: {
                "X-Authorization": user.token,
                'X-APP-VERSION': version,
                'Accept': "*/*",
                'Content-Length': file_size.toString(),
                'Connection': 'keep-alive',
                ...headers
              },
              // contentType: lookupMimeType(path),
            );
          if (!EnvConfig.isProd() || kDebugMode) {
            dio.interceptors.add(
              LogInterceptor(requestBody: true, responseBody: true),
            );
            dio.interceptors.add(DioLogInterceptor());
          }
          String uploadUrl = value.data['url'];
          dynamic signed_id = value.data['signed_id'];
          dio.put(
            uploadUrl,
            data: (await MultipartFile.fromFile(
              path,
              filename: file.path,
            ))
                .finalize(), //
            cancelToken: requestMap[id.toString()],
            onSendProgress: (int sent, int total) {
              LogUtil.v("sent:$sent,total:$total");
              // LogUtil.v("当前进度:${(sent / total) * 100}% 当前上传:${formatFileSize(sent)} ,总共${formatFileSize(total)}");
            },
          ).then((value) async {
            if ([201, 200].contains(value.statusCode)) {
              //上传成功 然后获取 signed_id 进行绑定货品
              await HttpUtil.commonRequest(
                getx.Get.context!,
                '/api/v1/backflows/${id}/attach_recording_video',
                isErrorToast: false,
                isCatchErrorLogin: false,
                isLoading: false,
                timeout: 120,
                method: HttpUtil.MethodPut,
                cancelToken: requestMap[id.toString()],
                queryParameters: {
                  "recording_video": signed_id,
                  "anchor_id": anchorId,
                },
              ).then((value) {
                if (value.statusCode == 200) {
                  CustomToast.show('上传讲解视频成功');

                  //绑定成功 删除本地资源
                  deleteLocalPage(id);
                  if (file.existsSync()) {
                    file.deleteSync();
                  }
                  if (success != null) {
                    success();
                  }
                }
              }).catchError((e) {
                Sentry.captureMessage(
                    "上传视频到阿里云成功,绑定视频到货品失败,货品id为:${id}:${e.toString()}");
              });
            }
          }).catchError((e) {
            DioError dioError = e as DioError;
            if (faild != null) {
              faild();
            }
            Sentry.captureMessage(
                "上传到阿里云失败,错误为,货品id为:${id}:${dioError.toString()}");
            if (dioError.response?.statusCode == 403) {
              CustomToast.show("暂无上传权限");
            } else {
              CustomToast.show(dioError.response?.data['msg'] ?? "上传失败");
            }
          });
        }).catchError((e) {
          Sentry.captureMessage("获取录播上传地址接口,货品id为:${id}，错误:${e.toString()}");
          if (faild != null) {
            faild();
          }
        });
      }
    });
  }

  //取消上传
  static cancel(String id) {
    LogUtil.v("取消请求");
    if (requestMap[id] != null && (!requestMap[id]!.isCancelled)) {
      requestMap[id]?.cancel();
      LogUtil.v("取消请求成功");
      CustomToast.show("取消上传成功,请稍后重新再试～");
      requestMap[id] = null;
    }
  }

  //全部取消上传
  static cancelAll() {
    LogUtil.v("取消全部请求");
    requestMap['12'] = CancelToken();
    requestMap.forEach((key, value) {
      if (value != null && (!value.isCancelled)) {
        value.cancel();
        LogUtil.v("取消请求成功");
        value = null;
      }
    });
    requestMap = {};
    LogUtil.v("requestMap:$requestMap");
  }

  static Future<List<FileSystemEntity>> getFileList() async {
    List<FileSystemEntity> result = [];
    if (await Permission.storage.request().isGranted) {
      String filePath = await getStoragePath();
      Directory directory = Directory(filePath);
      if (!directory.existsSync()) {
        directory.createSync();
      }
      List<FileSystemEntity> list = directory.listSync();
      List<FileSystemEntity> listFiles = [];
      if (list.length > 0) {
        list.forEach((element) {
          RegExp regExp = new RegExp("\.(mp4)", caseSensitive: false);
          if (regExp.hasMatch('$element')) {
            if (File(element.path).lengthSync() > 0) {
              LogUtil.v("视频文件:${element.path}");
              listFiles.add(element);
            }
          } else {
            LogUtil.v("不是视频文件:${element.path}");
          }
        });
      }
      if (info != null) {
        String infoStr = info?.fileName ?? "";
        int first = infoStr.lastIndexOf('/');
        int last = infoStr.lastIndexOf('.');
        String infoId = infoStr.substring(first + 1, last);
        listFiles.removeWhere((element) {
          String str = element.path;
          int first = str.lastIndexOf('/');
          int last = str.lastIndexOf('.');
          String id = str.substring(first + 1, last);
          LogUtil.v("element.path:${element.path}");
          LogUtil.v("info?.fileName:${info?.fileName}");
          return id == infoId;
        });
      }
      result = listFiles;
    }
    return result;
  }

  //清理录制视频
  static clearAll() async {
    if (await Permission.storage.request().isGranted) {
      String filePath = await getStoragePath();
      Directory directory = Directory(filePath);
      if (!directory.existsSync()) {
        directory.createSync();
      }
      List<FileSystemEntity> list = directory.listSync();
      if (list.length == 0) {
        CustomToast.show("当前没有缓存的录制视频,不需要清理");
        return;
      }
      if (list.length > 0) {
        list.forEach((element) {
          element.deleteSync();
        });
      }
      List<FileSystemEntity> list2 = directory.listSync();
      if (list2.length == 0) {
        CustomToast.show("清理录制视频成功");
      }
    } else {
      CustomToast.show("请打开本地文件读写权限");
    }
  }

  //每次登录时候检查主播当前目录是否存在文件 需要上传
  static checkFilesToUpload() async {
    UserProvider provider =
        Provider.of<UserProvider>(getx.Get.context!, listen: false);
    if (provider.isLogin) {
      if (provider.user!['id'] != 0) {
        //登录
        List maintain_studios = provider.user?['maintain_studios'] ?? [];
        if (maintain_studios.length > 0) {
          //主播
          if (await Permission.storage.request().isGranted) {
            checkDirectory();
          } else {
            CustomToast.show("请打开本地文件读写权限");
          }
        }
      }
    }
  }

  static checkDirectory() async {
    String filePath = await getStoragePath();
    Directory directory = Directory(filePath);
    if (!directory.existsSync()) {
      directory.createSync();
    }
    List<FileSystemEntity> list = directory.listSync();
    List<FileSystemEntity> listFiles = [];
    if (list.length > 0) {
      list.forEach((element) {
        RegExp regExp = new RegExp("\.(mp4)", caseSensitive: false);
        if (regExp.hasMatch('$element')) {
          LogUtil.v("视频文件:${element.path}");
          listFiles.add(element);
        } else {
          LogUtil.v("不是视频文件:${element.path}");
        }
      });
    }
    LogUtil.v("需要上传的文件:${listFiles.length}");
    if (listFiles.length == 0) {
      LogUtil.v("当前本地没有缓存视频，不需要上传");
      return;
    }
    for (int i = 0; i < listFiles.length; i++) {
      String str = listFiles[i].path;
      int first = str.lastIndexOf('/');
      int last = str.lastIndexOf('.');
      String id = str.substring(first + 1, last);
      upload(id, path: listFiles[i].path);
    }
  }
}
